package service

import (
	"code.huawei.com/tdc-sdk/utils"
	"github.com/astaxie/beego"
	"github.com/pkg/errors"
	"io/ioutil"
	"nft-server/common"
	"nft-server/config"
	"nft-server/dao"
	"nft-server/model"
	"nft-server/util"
	"time"
)

var (
	AdminUserName         = "root"
	AdminUserInitPassword = "Sac123456"
)

var AvailableQuantity *int

// GetLoginUserInfo 根据用户名获取用户的信息
func GetLoginUserInfo(userName string) (*common.LoginUserInfo, error) {
	queryUserParameter := common.QueryUserDetail{
		UserName: &userName,
	}
	user, err := dao.GetUserDao().GetUserInformation(&queryUserParameter)
	// 查询数据库出错
	if err != nil {
		return nil, errors.WithMessage(err, "Query database error")
	}
	// 没有查询指定用户名的数据
	if user == nil {
		return nil, nil
	}
	loginUserInfo := &common.LoginUserInfo{
		Id:            user.Id,
		Name:          user.Name,
		Password:      user.Password,
		Salt:          user.Salt,
		FailTimes:     user.FailTimes,
		Locked:        user.Locked,
		LockedTime:    user.LockedTime,
		Role:          user.Role.Name,
		PwdChanged:    user.PwdChanged,
		Authenticated: user.Authenticated,
	}
	return loginUserInfo, nil
}

// GetUserProfile 用户个人信息页面，根据用户名获取用户的个人信息
func GetUserProfile(userName string) (*common.Profile, error) {
	queryUserParameter := common.QueryUserDetail{
		UserName: &userName,
	}
	user, err := dao.GetUserDao().GetUserInformation(&queryUserParameter)
	// 查询数据库出错
	if err != nil {
		return nil, errors.WithMessage(err, "Query database error")
	}
	// 没有查询指定用户名的数据
	if user == nil {
		return nil, nil
	}
	regPhoneNum := ""
	if user.PhoneNumber != "" && len(user.PhoneNumber) == util.PhoneNumLen {
		regPhoneNum = user.PhoneNumber[0:3]
		regPhoneNum += "****"
		regPhoneNum += user.PhoneNumber[7:11]
	}
	ProfilePhotoUrl := ""
	if user.ProfilePhotoUrl != "" {
		ProfilePhotoUrl, err = util.GetAssetUrl(user.ProfilePhotoUrl)
		if err != nil {
			return nil, errors.WithMessage(err, "GetAssetUrl error")
		}
	}
	userProfile := &common.Profile{
		Name:            user.Name,
		Address:         user.Address,
		CreatedTime:     user.CreatedTime.UnixMilli(),
		Authenticated:   user.Authenticated,
		RegPhoneNum:     regPhoneNum,
		ProfilePhotoUrl: ProfilePhotoUrl,
	}
	return userProfile, nil
}

// GetUserAddressById 如果查询不到地址返回空字符串
func GetUserAddressById(userId int) (string, error) {
	queryUserParameter := common.QueryUserDetail{
		UserId: &userId,
	}
	user, err := dao.GetUserDao().GetUserInformation(&queryUserParameter)
	// 查询数据库出错
	if err != nil {
		return "", errors.WithMessage(err, "Query database error")
	}
	if user == nil {
		return "", nil
	}

	return user.Address, nil
}

func GetUserInfoByPhoneNum(phoneNum string) (*common.LoginUserInfo, error) {
	queryUserParameter := common.QueryUserDetail{
		PhoneNumber: &phoneNum,
	}
	user, err := dao.GetUserDao().GetUserInformation(&queryUserParameter)
	// 查询数据库出错
	if err != nil {
		return nil, errors.WithMessage(err, "Query database error")
	}
	// 没有查询指定用户名的数据
	if user == nil {
		return nil, nil
	}
	loginUserInfo := &common.LoginUserInfo{
		Id:            user.Id,
		Name:          user.Name,
		Password:      user.Password,
		Salt:          user.Salt,
		FailTimes:     user.FailTimes,
		Locked:        user.Locked,
		LockedTime:    user.LockedTime,
		Role:          user.Role.Name,
		PwdChanged:    user.PwdChanged,
		Authenticated: user.Authenticated,
	}
	return loginUserInfo, nil
}

// Unsubscribe 根据用户id注销用户
func Unsubscribe(userId int) error {
	if err := dao.GetUserDao().DeleteUserInfo(userId); err != nil {
		return errors.Errorf("Unsubscribe error: %v", err)
	}
	return nil
}

// CreatUser 用户注册时，通过用户名和密码创建新用户
func CreatUser(username, password, phoneNum string) error {
	// DAC上生成用户信息
	cert, key, addr, err := GenerateIdentity(username)
	if err != nil {
		return errors.Errorf("GenerateIdentity error: %v", err)
	}
	// 生成salt对密码进行加密
	salt, encryptPass, err := util.EncryptPassword(password, common.Iter, common.KeyLength)
	if err != nil {
		return err
	}
	user := &model.User{
		Name:             username,
		Password:         encryptPass,
		Salt:             salt,
		PhoneNumber:      phoneNum,
		Address:          addr,
		Key:              key,
		Cert:             cert,
		FailTimes:        0,
		Locked:           false,
		LockedTime:       time.Time{},
		CreatedTime:      time.Now(),
		UpdateTime:       time.Now(),
		PwdChanged:       false,
		Role:             UserRole,
		Authenticated:    false,
		AssetObtainedNum: 0,
	}

	err = dao.GetUserDao().SaveUserInfo(user)
	if err != nil {
		return errors.Errorf("SaveUserInfo error: %v", err)
	}
	return nil
}

func UpdatePassword(userId int, password, salt string) error {
	updateUserParameters := common.ModifyUserDetail{
		UserId:   userId,
		Password: &password,
		Salt:     &salt,
	}
	err := dao.GetUserDao().UpdateAllUserInfo(&updateUserParameters)
	if err != nil {
		return err
	}
	return nil
}

func GetUserNum() (int, error) {
	queryNumParameter := common.QueryUserNum{}
	totalNum, err := dao.GetUserDao().GetUserNum(&queryNumParameter)
	if err != nil {
		return 0, errors.Errorf("GetUserNum error: %v", err)
	}
	return totalNum, nil
}

// GetUsers 分页获取所有的用户
func GetUsers(pageSize, pageIndex int) ([]*common.User, error) {
	offset := (pageIndex - 1) * pageSize
	queryUserListParameter := common.QueryUserList{
		PageSize: pageSize,
		Offset:   offset,
	}
	userModels, err := dao.GetUserDao().GetUserList(&queryUserListParameter)
	if err != nil {
		return nil, errors.Errorf("GetUserList error: %v", err)
	}
	var users []*common.User
	for _, value := range userModels {
		tmpUser := &common.User{
			UserId:        value.Id,
			UserName:      value.Name,
			Locked:        value.Locked,
			Role:          value.Role.Name,
			Authenticated: value.Authenticated,
		}
		users = append(users, tmpUser)
	}
	return users, nil
}

func GetUserById(userId int) (*common.User, error) {
	queryUserParameter := common.QueryUserDetail{
		UserId: &userId,
	}
	user, err := dao.GetUserDao().GetUserInformation(&queryUserParameter)
	if err != nil {
		return nil, errors.Errorf("GetUserById error: fail to get user by id")
	}
	if user == nil {
		return nil, nil
	}
	res := &common.User{
		UserId:        user.Id,
		UserName:      user.Name,
		Locked:        user.Locked,
		Role:          user.Role.Name,
		Authenticated: user.Authenticated,
	}
	return res, nil
}

func GetUserInfoByAddress(userAddress string) (*common.User, error) {
	queryUserParameter := common.QueryUserDetail{
		Address: &userAddress,
	}
	user, err := dao.GetUserDao().GetUserInformation(&queryUserParameter)
	if err != nil {
		return nil, errors.Errorf("GetUserById error: fail to get user by user address")
	}
	if user == nil {
		return nil, nil
	}
	res := &common.User{
		UserId:        user.Id,
		UserName:      user.Name,
		Locked:        user.Locked,
		Role:          user.Role.Name,
		Authenticated: user.Authenticated,
	}
	return res, nil
}

// GetUsersByMatchingName  根据用户名模糊匹配分页获取所有用户
func GetUsersByMatchingName(userName string, pageIndex, pageSize int) ([]*common.User, error) {
	offset := (pageIndex - 1) * pageSize
	queryUserList := common.QueryUserList{
		PageSize: pageSize,
		Offset:   offset,
		UserName: &userName,
	}
	userModels, err := dao.GetUserDao().GetUserList(&queryUserList)
	if err != nil {
		return nil, errors.Errorf("GetUsersByMatchingName error: %v", err)
	}
	var users []*common.User
	for _, value := range userModels {
		tmpUser := &common.User{
			UserId:        value.Id,
			UserName:      value.Name,
			Locked:        value.Locked,
			Role:          value.Role.Name,
			Authenticated: value.Authenticated,
		}
		users = append(users, tmpUser)
	}
	return users, nil
}

// GetUserNumByMatchingName  根据用户名模糊匹配分页获取用户的数量
func GetUserNumByMatchingName(name string) (int, error) {
	queryNumParameter := common.QueryUserNum{
		UserName: &name,
	}
	totalNum, err := dao.GetUserDao().GetUserNum(&queryNumParameter)
	if err != nil {
		return 0, errors.Errorf("GetUserNumByMatchingName error: %v", err)
	}
	return totalNum, nil
}

// GetUsersByStatus 根据用户是否被锁定分页获取用户列表
func GetUsersByStatus(locked bool, pageIndex, pageSize int) ([]*common.User, error) {
	offset := (pageIndex - 1) * pageSize
	queryUserListParameter := common.QueryUserList{
		Locked:   &locked,
		PageSize: pageSize,
		Offset:   offset,
	}
	userModels, err := dao.GetUserDao().GetUserList(&queryUserListParameter)
	if err != nil {
		return nil, errors.Errorf("GetUsersByStatus error: %v", err)
	}
	var users []*common.User
	for _, value := range userModels {
		tmpUser := &common.User{
			UserId:        value.Id,
			UserName:      value.Name,
			Locked:        value.Locked,
			Role:          value.Role.Name,
			Authenticated: value.Authenticated,
		}
		users = append(users, tmpUser)
	}
	return users, nil
}

// GetUserNumByStatus 根据用户状态获取用户数量
func GetUserNumByStatus(locked bool) (int, error) {
	queryUserNum := common.QueryUserNum{
		Locked: &locked,
	}
	totalNum, err := dao.GetUserDao().GetUserNum(&queryUserNum)
	if err != nil {
		return 0, errors.Errorf("GetUserNumByStatus error: %v", err)
	}
	return totalNum, nil
}

// GetUsersByNameAndStatus 根据用户名和用户状态获取用户列表
func GetUsersByNameAndStatus(userName string, locked bool, pageIndex, pageSize int) ([]*common.User, error) {
	offset := (pageIndex - 1) * pageSize
	queryUserListParameter := common.QueryUserList{
		UserName: &userName,
		Locked:   &locked,
		PageSize: pageSize,
		Offset:   offset,
	}
	userModels, err := dao.GetUserDao().GetUserList(&queryUserListParameter)
	if err != nil {
		return nil, errors.Errorf("GetUsersByNameAndStatus error: %v", err)
	}
	var users []*common.User
	for _, value := range userModels {
		tmpUser := &common.User{
			UserId:        value.Id,
			UserName:      value.Name,
			Locked:        value.Locked,
			Role:          value.Role.Name,
			Authenticated: value.Authenticated,
		}
		users = append(users, tmpUser)
	}
	return users, nil
}

// GetUserNumByNameAndStatus 根据用户名和用户状态获取用户数量
func GetUserNumByNameAndStatus(name string, locked bool) (int, error) {
	queryNumParameter := common.QueryUserNum{
		UserName: &name,
		Locked:   &locked,
	}
	totalNum, err := dao.GetUserDao().GetUserNum(&queryNumParameter)
	if err != nil {
		return 0, errors.Errorf("GetUserNumByNameAndStatus error: %v", err)
	}
	return totalNum, nil
}

func DeleteUserById(userId int) error {
	err := dao.GetUserDao().DeleteUserInfo(userId)
	if err != nil {
		return errors.Errorf("DeleteUserById error: %v", err)
	}
	return nil
}

func UpdateLockedById(userId int, locked bool) (*common.User, error) {
	updateUserParameters := common.ModifyUserDetail{
		UserId: userId,
		Lock:   &locked,
	}
	err := dao.GetUserDao().UpdateAllUserInfo(&updateUserParameters)
	if err != nil {
		return nil, errors.Errorf("UpdateLockedById error: %v", err)
	}
	user, err := GetUserById(userId)
	if err != nil {
		return nil, errors.Errorf("UpdateLockedById error: %v", err)
	}
	return user, nil
}
func GetAdminId() (int, error) {
	admin, err := dao.GetUserDao().GetAdminUser()
	if err != nil {
		return -1, errors.Errorf("GetAdminId error: %v", err)
	}
	return admin.Id, nil
}

func GetAdmin() (*model.User, error) {
	admin, err := dao.GetUserDao().GetAdminUser()
	if err != nil {
		return nil, errors.Errorf("GetAdminId error: %v", err)
	}
	return admin, nil
}

func UpdateLockedTime(userId int, time time.Time) error {
	updateUserParameters := common.ModifyUserDetail{
		UserId:   userId,
		LockTime: &time,
	}
	err := dao.GetUserDao().UpdateAllUserInfo(&updateUserParameters)
	if err != nil {
		errors.Errorf("UpdateFailInfo error: %v", err)
	}
	return nil
}
func UpdateFailTimes(userId int, failTimes int) error {
	updateUserParameters := common.ModifyUserDetail{
		UserId:    userId,
		FailTimes: &failTimes,
	}
	err := dao.GetUserDao().UpdateAllUserInfo(&updateUserParameters)
	if err != nil {
		errors.Errorf("UpdateFailInfo error: %v", err)
	}
	return nil
}

// InitAdminUser 系统初始化时，初始化管理员账号
func InitAdminUser() error {
	queryUserParameter := common.QueryUserDetail{
		UserName: &AdminUserName,
	}
	admin, err := dao.GetUserDao().GetUserInformation(&queryUserParameter)
	if err != nil {
		return errors.Errorf("CreateAdminUser error: %v", err)
	}
	if admin != nil {
		return nil
	}
	dacConfig, err := utils.NewConfig(config.GetConfig().DACConfig.ConfigPath)
	if err != nil {
		return errors.Errorf("CreateAdminUser error: %v", err)
	}
	salt, encryptedPass, err := util.EncryptPassword(AdminUserInitPassword, common.Iter, common.KeyLength)
	if err != nil {
		return errors.Errorf("CreateAdminUser error: %v", err)
	}

	key, err := ioutil.ReadFile(dacConfig.Identities[0].KeyPath)
	if err != nil {
		return errors.Errorf("CreateAdminUser error: %v", err)
	}
	cert, err := ioutil.ReadFile(dacConfig.Identities[0].CertPath)
	if err != nil {
		return errors.Errorf("CreateAdminUser error: %v", err)
	}

	admin = &model.User{
		Name:             AdminUserName,
		Password:         encryptedPass,
		Salt:             salt,
		Address:          "",
		Key:              string(key),
		Cert:             string(cert),
		FailTimes:        0,
		Locked:           false,
		LockedTime:       time.Time{},
		CreatedTime:      time.Now(),
		UpdateTime:       time.Now(),
		PwdChanged:       false,
		Role:             AdminRole,
		Authenticated:    true,
		AssetObtainedNum: 0,
	}
	if err := dao.GetUserDao().SaveUserInfo(admin); err != nil {
		return errors.Errorf("CreateAdminUser error: %v", err)
	}
	return nil
}

func JudgeAdminUser(userId int) error {
	admin, err := dao.GetUserDao().GetAdminUser()
	if err != nil {
		return err
	}
	if userId == admin.Id {
		return errors.Errorf("current user is admin.")
	}
	return nil
}

func UpdateAuthentication(userId int, valid bool) error {
	updateUserParameters := common.ModifyUserDetail{
		UserId:        userId,
		Authenticated: &valid,
	}
	err := dao.GetUserDao().UpdateAllUserInfo(&updateUserParameters)
	if err != nil {
		return err
	}
	return nil
}

// GetUserAssetNum 获得当前数据库中用户已领取的资产数量
func GetUserAssetNum(userId int) (int, error) {
	queryUserParameter := common.QueryUserDetail{
		UserId: &userId,
	}
	user, err := dao.GetUserDao().GetUserInformation(&queryUserParameter)
	if err != nil {
		return util.InvalidNumber, errors.WithMessage(err, "Query database error")
	}
	return user.AssetObtainedNum, nil
}

// CheckApplyNum 确定当前用户是否有权限领取新的资产
func CheckApplyNum(userId int) (bool, error) {
	if AvailableQuantity == nil {
		num, err := beego.AppConfig.Int("availableQuantity")
		if err != nil {
			return false, err
		}
		AvailableQuantity = &num
	}
	currentObtainedAssetNum, err := GetUserAssetNum(userId)
	if err != nil || currentObtainedAssetNum == -1 {
		return false, err
	}

	if *AvailableQuantity > currentObtainedAssetNum {
		return true, nil
	}
	return false, nil
}

// UpdateAssetNum 更新数据库中已获得的资产数
func UpdateAssetNum(userId, newlyObtainedAssetNum int) error {
	updateUserParameters := common.ModifyUserDetail{
		UserId:           userId,
		AssetObtainedNum: &newlyObtainedAssetNum,
	}
	err := dao.GetUserDao().UpdateAllUserInfo(&updateUserParameters)
	if err != nil {
		return err
	}
	return nil
}

func FindAuthenticationInfo(userId int) (bool, error) {
	var err error
	if userId <= 0 {
		return false, errors.New("User id 错误")
	}
	userInfo, err := GetUserById(userId)
	if err != nil {
		return false, errors.New("查询用户失败")
	}
	if userInfo == nil {
		return false, errors.New("查询用户id不存在")
	}
	userAuthenticatedInfo := userInfo.Authenticated
	if userAuthenticatedInfo {
		return true, nil
	} else {
		return false, nil
	}
}

func GetUserAllInfoById(userId int) (*model.User, error) {
	queryUserParameter := common.QueryUserDetail{
		UserId: &userId,
	}
	user, err := dao.GetUserDao().GetUserInformation(&queryUserParameter)
	if err != nil {
		return nil, errors.Errorf("GetUserAllInfoById error: fail to get user by id")
	}
	return user, nil
}

func UpdateProfilePhoto(userId int, profilePhotoUrl string) error {
	updateUserParameters := common.ModifyUserDetail{
		UserId:          userId,
		ProfilePhotoUrl: &profilePhotoUrl,
	}
	err := dao.GetUserDao().UpdateAllUserInfo(&updateUserParameters)
	if err != nil {
		return err
	}
	return nil
}
